#pragma once
#include <iostream>
#include <unistd.h>
#include <string>
#include <vector>
#include <sstream>
#include <unordered_map>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <algorithm>
#include <fcntl.h>
#include <sys/sendfile.h>
#include <wait.h>
#include <cstdlib>
#include "Unit.hpp"


#define WEB_ROOT "wwwroot"
#define HOME_PAGE "index.html"
#define PAGE_404 "404.html"
#define PAGE_400 "400.html"
#define PAGE_500 "500.html"
#define SEP ": "
#define HTTP_VERSION "Http/1.0"
#define LINE_END "\r\n"

// WEB根目录和首页

enum ERROR_CODE
{
   OK=200,
   NOT_FOUND=404,
   BAD_REQUEST=400,
   SERVER_ERROR = 500
};

namespace wyl
{
    // 根据状态码返回状态描述字符串
    std::string Code2Desc(int status)
    {
        std::string desc;
        switch (status)
        {
        case OK:
            desc = "OK";
            break;
        case NOT_FOUND:
            desc = "NO FOUND!";
            break;
        case BAD_REQUEST: 
            desc = "BAD REQUEST!";
            break; 
        case SERVER_ERROR: 
            desc = "SERVER ERROR!" ;
            break;
        default:
            break;
        }
        return desc;
    }

    // Content-Type的映射
    static std::string Suffix2Desc(std::string &str)
    {
        static std::unordered_map<std::string, std::string> map =
            {
                {".html", "text/html"}, {".jpg", "application/x-jpg"}, {".js", "application/x-javascript"}, {".css", "text/css"}};
        auto it = map.find(str);
        if (it != map.end())
        {
            return it->second;
        }
        return "text/html";
    }

    // 请求类
    class HttpRequest
    {
    public:
        // 接收
        std::string _http_request_line;                // 请求行
        std::vector<std::string> _http_request_header; // 请求报头
        std::string _blank;                            // 空行
        std::string _http_request_body;                // 请求正文

        // 解析
        std::string _method;  // 请求方法
        std::string _uri;     // 请求资源的uri ,GET方法 : 资源路径 + ？ + GET上传的数据
        std::string _version; // 协议版本
        std::unordered_map<std::string, std::string> _header_map;

        std::string _path;         // 资源路径
        std::string _query_string; // GET上传的数据
        std::string _suffix;       // 资源后缀

        // Content-Length
        size_t _ContentLength;
        bool _http_request_cgi; // 是否用cgi模式处理请求
        HttpRequest() : _ContentLength(0), _http_request_cgi(false) {}
        ~HttpRequest() {}
    };

    // 响应类
    class HttpResponse
    {
    public:
        std::string _http_response_statline;            // 响应状态行
        std::vector<std::string> _http_response_header; // 响应报头
        std::string _blank;                             // 空行
        std::string _http_response_body;                // 响应正文

        int _status_code; // 状态码
        int _fd;          // 打开资源的文件描述符
        size_t _size;     // 资源的大小
        HttpResponse() : _status_code(OK), _fd(-1), _size(0), _blank("\r\n") {}
        ~HttpResponse() {}
    };

    // 对端类,用于读取请求，分析请求，构建响应，IO通信
    class Endpoint
    {
    private:
        HttpRequest _http_request;   // 请求类
        HttpResponse _http_response; // 响应类
        int _sock;
        bool _IsStop; //是否停止

    private:
        // 读取请求行
        bool recv_http_request_line()
        {
            std::string &line = _http_request._http_request_line;
            if(Unit::ReadLine(_sock, line) <= 0 )
            {
              //读取失败
              _IsStop = true; 
            }
            else 
            {
                //读取成功
               line.resize(line.size() - 1); // 去掉请求行的空行
                LOG(INFO,line);
            }
            return _IsStop;
        }
        // 读取请求报头
        bool recv_http_request_header()
        {
            std::string line;
            while (true)
            {
                if(Unit::ReadLine(_sock, line) <= 0 ) 
                {
                  //读取失败
                  _IsStop = true; 
                  break;
                }
                if (line == "\n") // 如果读到空行
                {
                    _http_request._blank = line;
                    break;
                }
                line.resize(line.size() - 1);                       // 去掉读取到的空行
                _http_request._http_request_header.push_back(line); // 加上该行
                //     LOG(INFO,line);
            }
            return _IsStop ;
        }

        // 是否需要读取正文
        bool is_need_recv_http_request_body()
        {
            auto &method = _http_request._method;
            if (method == "POST") // POST方法才有可能有正文
            {
                auto &map = _http_request._header_map;
                std::unordered_map<std::string, std::string>::iterator it = map.find("Content-Length");
                // 找到了
                if (it != map.end())
                {
                    _http_request._ContentLength = atoi(it->second.c_str()); // 把Content-Length的value值赋值给_ContentLength
                    return true;
                }
                else
                    return false; // 正文为0
            }
            // GET方法，不需要解析
            return false;
        }

        // 读取正文
        bool recv_http_request_body()
        {
            if (is_need_recv_http_request_body()) // 是否需要读取
            {
                size_t cl = _http_request._ContentLength;
                char ch = 0;
                while (cl)
                {
                    ssize_t s = recv(_sock, &ch, 1, 0); // 读取一个字节
                    if (s > 0)
                    {
                        // 读取成功
                        _http_request._http_request_body += ch;
                    }
                    else 
                    {
                       //读取失败 
                       _IsStop = true;   
                       break;
                    }
                    cl--;
                }
            }
            return _IsStop ;
        }

        // 解析请求行
        void prse_http_request_line()
        {
            // 创建一个stringstream对象
            std::stringstream ss(_http_request._http_request_line);
            // 用空格来把ss区分成三个字符串， GET / HTTP/1.1 区分成 GET , / , HTTP/1.1
            ss >> _http_request._method >> _http_request._uri >> _http_request._version;
            // 大小写转换
            std::string &method = _http_request._method;
            std::transform(method.begin(), method.end(), method.begin(), ::toupper);
        }
        void prse_http_request_header()
        {
            std::string key;
            std::string value;
            // 遍历接收到的报头
            for (auto &e : _http_request._http_request_header)
            {
                // 字符串切分
                if (Unit::CutString(e, key, value, SEP))
                {
                    _http_request._header_map[key] = value; // key value放进map中
                }
            }
        }


        void HandlerError(std::string page)
        {
            _http_request._http_request_cgi = false; //只要出错就用非cgi方式发送 
            // 状态码为404
            // 打开文件
            
            //设置路径
            std::string path_tmp = page;
            page = WEB_ROOT;
            page += "/";
            page += path_tmp; 
            _http_response._fd = open(page.c_str(), O_RDONLY);
            if (_http_response._fd >= 0)
            {
                // stat
                struct stat st;
                stat(page.c_str(), &st);

                _http_response._size = st.st_size;
                // 添加响应报头
                std::string line;
                line = "Content-Type: text/html"; // Content-Type
                line += LINE_END;                 // 换行
                _http_response._http_response_header.push_back(line);

                // Content-Length报头属性
                line = "Content-Length: ";
                line += std::to_string(st.st_size);
                line += LINE_END;
                _http_response._http_response_header.push_back(line);
            }
        }

        void BuildOKResponse()
        {
          //构建OK响应报文
          std::string line = "Content-Type: "; 
          line += Suffix2Desc(_http_request._suffix); 
          line += LINE_END ; 
          _http_response._http_response_header.push_back(line);
          line = "Content-Length: "; 
          if(_http_request._http_request_cgi) 
          {
            //cgi模式
            line += std::to_string(_http_response._http_response_body.size()) ;
          }
          else 
          {
            line += std::to_string(_http_response._size) ;
          }
          line += LINE_END;
          _http_response._http_response_header.push_back(line);
        }  


        //
        void BuildHttpResponseHelper()
        {
            // 状态响应行
            int code = _http_response._status_code;
            std::string &line = _http_response._http_response_statline;
            line += HTTP_VERSION; // 协议版本
            line += " ";
            line += std::to_string(code); // 状态码
            line += " ";
            line += Code2Desc(code); // 状态描述
            line += LINE_END;
            switch (code)
            {
            case OK:
                BuildOKResponse();
                break;
            case NOT_FOUND:
                HandlerError(PAGE_404);
                break;
            case BAD_REQUEST:
                HandlerError(PAGE_400);
            case SERVER_ERROR:
                HandlerError(PAGE_500);
            default:
                break;
            }
        }

        int ProcessNonCgi()
        {
            // 读取文件
            _http_response._fd = open(_http_request._path.c_str(), O_RDONLY);
            std::cout << "line 295 : _http_response._fd = " << _http_response._fd << std::endl;
            // 打开文件成功
            if (_http_response._fd > 0)
            {
                return OK;
            }
              return BAD_REQUEST; //打开文件失败，请求错误
        }

        int ProcessCgi()
        {
            std::cout << "Cgi model " << std::endl;
            int code = OK;
            auto &bin = _http_request._path;                         // 资源路径
            std::string &method = _http_request._method;             // 请求方法
            std::string &query_string = _http_request._query_string; // GET方法的数据
            int content_length = _http_request._ContentLength;
            std::string &response_body = _http_response._http_response_body;
            std::string &request_body = _http_request._http_request_body;

            std::string method_env;
            std::string query_string_env;
            std::string content_length_env;
            int input[2];  // 以父进程的角度，读
            int output[2]; // 以父进程的角度，写

            // 创建管道1
            if (pipe(input) < 0)
            {
                LOG(ERROR, "create input pipe error");
                code = SERVER_ERROR; //服务器错误
                return code;
            }
            // 创建管道2
            if (pipe(output) < 0)
            {
                LOG(ERROR, "create output pipe error");
                code = SERVER_ERROR;
                return code;
            }
            pid_t pid = fork();
            if (pid > 0)
            {
                // father
                // 父进程，input关闭写，output关闭读
                close(input[1]);
                close(output[0]);

                // 如果请求的是POST方法，那么要把正文写入管道
                if ("POST" == method)
                {
                    int size = 0;
                    int total = 0;
                    const char *start = request_body.c_str();
                    // 请求正文写入管道
                    while (total < content_length && (size = write(output[1], start + total, request_body.size() - total)) > 0)
                    {
                        total += size;
                    }
                }

                // 读取 CGI程序发过来的数据
                char ch = 0;
                while (read(input[0], &ch, 1) > 0)
                {
                    // 读取成功
                    response_body += ch;  //响应正文 + 上读取的字符
                }

                
                int status;
                pid_t ret = waitpid(pid, &status, 0); // waitpid成功返回子进程的pid，否则返回-1
                if (ret == pid)
                {
                    // 等待成功
                    if (WIFEXITED(status)) // 正常返回true ， 如果收到信号返回false
                    {
                        if (WEXITSTATUS(status) == 0) // 状态码是否为0
                        {
                            code = OK;
                        }
                        else
                        {
                            code = BAD_REQUEST; // 请求错误
                        }
                    }
                    else
                    {
                        code = SERVER_ERROR; //服务器错误
                    }
                }

                close(input[0]);
                close(output[1]);
            }
            else if (pid == 0)
            {
                // child
                // 子进程，input关闭读，output关闭写
                close(input[0]);
                close(output[1]);

                // 程序替换会替换代码和数据，打开的文件依然存在，但是input和ouput数据不会存在
                // 子进程要知道请求的方法，所以需要导入一个环境变量
                method_env = "METHOD=";
                method_env += method; // METHOD=GET or METHOD=POST
                putenv((char *)method_env.c_str());

                // 如果是GET方法
                if ("GET" == method)
                {
                    query_string_env = "QUERY_STRING=";
                    query_string_env += query_string; // QUERY_STRING=上传的数据
                    putenv((char *)query_string_env.c_str());
                }
                else if ("POST" == method)
                {
                    // POST方法请求，传Content-Length
                    content_length_env = "CONTENT_LENGTH=";
                    content_length_env += std::to_string(content_length);
                    putenv((char *)content_length_env.c_str());
                }

                // input[1] -> 1 写出
                // output[0]-> 0 读入
                dup2(input[1], 1); // 重定向，把fd为1的文件替换成文件描述符input[1]的文件
                dup2(output[0], 0);

                // 程序替换-
                execl(bin.c_str(), bin.c_str(), nullptr); // 把当前进程替换成bin路径上的程序
                exit(1);
            }
            else
            {
                // erro
                LOG(ERROR, "fork error");
                return SERVER_ERROR; //fork失败，服务器错误
            }

            return code;
        }

    public:
        Endpoint(int sock) : _sock(sock),_IsStop(false) {}
        ~Endpoint()
        {
            close(_sock);
        }
        
        bool IsStop()
        {
          return _IsStop;
        }

        // 接收请求
        void RecvHttpRequest()
        {
          if((!recv_http_request_line())  && (! recv_http_request_header())) 
          {
            //接收请求行和请求报头没有出错
            prse_http_request_line();   // 解析请求行
            prse_http_request_header(); // 解析请求报头
            recv_http_request_body();   // 接收正文
           }
        }
        // 构建响应
        void BulidHttpResponse()
        {
            int &code = _http_response._status_code; // 状态码
            struct stat st;                          // stat
            std::string tmp_path;
            size_t found; //.的下标
            // 如果请求的方法不是GET也不是POST
            if (_http_request._method != "GET" && _http_request._method != "POST")
            {
                std::cout << "line 459 : code = " << code << std::endl; 
                code = BAD_REQUEST; //请求错误
                LOG(WARING, "NOT FOUND");
                goto END;
            }
            // 请求的是GET方法
            if (_http_request._method == "GET")
            {
                if (!Unit::CutString(_http_request._uri, _http_request._path, _http_request._query_string, "?"))
                {
                    // 字符串切分失败，path = uri
                    _http_request._path = _http_request._uri;
                }
                else
                {
                    // 字符串切分成功，采用cgi模式处理请求
                    _http_request._http_request_cgi = true;
                }
            }
            else if (_http_request._method == "POST")
            {

                // POST
                _http_request._http_request_cgi = true;
                // 请求地址更新
                _http_request._path = _http_request._uri;
            }
            else
            {
                // do nothing
            }

            tmp_path = _http_request._path;
            _http_request._path = WEB_ROOT;
            _http_request._path += tmp_path;
           
            // 如果访问的是根目录，那么加上首页信息
            if (_http_request._path[_http_request._path.size() - 1] == '/')
            {
                _http_request._path += HOME_PAGE;
            }
            // 判断访问的资源是否存在
            if (stat(_http_request._path.c_str(), &st) == 0) // 存在返回0 .不存在返回-1
            {
                // 访问的资源存在
                // 判断访问的资源是否是目录
                if (S_ISDIR(st.st_mode)) // 如果为真，则代表该文件是目录
                {
                    // 访问的文件是目录的话，那么就访问该目录下的index.html
                    _http_request._path += "/";
                    _http_request._path += HOME_PAGE;
                    stat(_http_request._path.c_str(), &st);
                }
                _http_response._size = st.st_size;
                // 访问的资源是否具有可执行权限
                if ((st.st_mode & S_IXUSR) || (st.st_mode & S_IXGRP) || (st.st_mode & S_IXOTH))
                {
                    _http_request._http_request_cgi = true;
                }
            }
            else
            {
               // 访问的资源不存在，默认为当前路径的index.html
                std::string message;
                message += _http_request._path;
                message += " : NOT FOUND!";
                LOG(WARING, message);
            }

            // 提取后缀
            found = _http_request._path.rfind("."); // 从后往前找第一个.
            if (found == std::string::npos)
            {
                // 没找到
                _http_request._suffix = ".html";
            }
            else
            {
                // 找到了
                _http_request._suffix = _http_request._path.substr(found);
            }

            if (_http_request._http_request_cgi)
            {
                // CGI模式处理
                code = ProcessCgi();
            }
            else
            {
                // 非CGI处理
                code = ProcessNonCgi();
            }
        END:
            std::cout << "请求的资源：" << _http_request._path <<"   后缀名是：" << _http_request._suffix << "  请求的大小是 ： " << _http_response._size  << std::endl;
            BuildHttpResponseHelper();
        }
        // 发送响应
        void SendHttpResponse()
        {
            // 发送状态行
            send(_sock, _http_response._http_response_statline.c_str(), _http_response._http_response_statline.size(), 0);

            // 发送响应报头
            for (auto &e : _http_response._http_response_header)
            {
                send(_sock, e.c_str(), e.size(), 0);
            }

            // 发送空行
            send(_sock, _http_response._blank.c_str(), _http_response._blank.size(), 0);
            
            if(_http_request._http_request_cgi) 
            {
                //cgi 模式，正文部分在 body中存着
                size_t size = 0 ;
                size_t total = 0; 
                std::string& body = _http_response._http_response_body;
                const char* start = body.c_str(); 
                
                while((total < body.size()) && (size = (send(_sock,start + size,body.size(),0)))) 
                {
                    total += size;
                }
            } else 
             {
                // 非cgi模式,发送的是文件
                  sendfile(_sock, _http_response._fd, 0, _http_response._size); // 把fd描述符的内容拷贝到sock
                  close(_http_response._fd);
             }
        }
    
    };

    // 入口类
    class CallBack
    {
    public:
        CallBack(){}  
        
        //仿函数
        void operator()(int sock) 
        {
          HandlerRequest(sock); 
        }
        // 处理请求
        void HandlerRequest(int sock)
        {
            LOG(INFO,"get a new connect : fd = " +std::to_string(sock));
// #define DEBUG 1
#ifdef DEBUG
            char buff[1024 * 10];
            recv(sock, buff, sizeof buff - 1, 0);
            std::cout << "---------------------------------------" << std::endl;
            std::cout << buff << std::endl;
            std::cout << "---------------------------------------" << std::endl;
#else
            Endpoint *ep = new Endpoint(sock);
            ep->RecvHttpRequest();   // 接收请求
            if(!ep->IsStop())
            {
              std::cout << " line 664 , stop = " << ep->IsStop()<<std::endl;
              //读取没有出错的情况
               ep->BulidHttpResponse(); // 构建响应
               ep->SendHttpResponse();  // 发送响应
               LOG(INFO, "Recv Success , Begain Build and Send !"); 
            }else 
            {
              //读取出错的情况
              LOG(WARING, "Recv Error !, Not Build and Send! ") ;
            }
            delete ep;
#endif
            close(sock);
        }
        ~CallBack(){}  
    };
};
    
